#include "pch.h"
#include "RenderViewport.h"
#include "Camera.h"
#include "RendererEngine.h"

namespace mini
{
	CRenderViewport::CRenderViewport()
	{
		mType = EntityType::RenderViewPort;
	}

	CRenderViewport::~CRenderViewport()
	{
		SafeRelease(mpRenderToTexture);
		SafeRelease(mpCamera);
		SafeRelease(mpMaterialUnit);
	}

	bool CRenderViewport::initViewPortSize(int width, int height)
	{
		SafeRelease(mpRenderToTexture);
		mpRenderToTexture = new CRenderToTexture(width, height);
		return mpRenderToTexture->createBuffer(DXGI_FORMAT_D24_UNORM_S8_UINT, D3D11_BIND_DEPTH_STENCIL, DXGI_FORMAT_R32G32B32A32_FLOAT);
	}

	void CRenderViewport::buildCamera(CCamera* pCamera)
	{
		SafeRelease(mpCamera);
		mpCamera = pCamera;
		mpCamera->AddRef();
	}

	void CRenderViewport::createBuildCamera(const std::string& cameraName, const CPoint3F& eye, const CPoint3F& lookat)
	{
		mini::CCamera* pCamera = dynamic_cast<mini::CCamera*>(mini::GEngine->createActor(mini::EntityType::Camera));
		pCamera->setName(cameraName);
		pCamera->setCameraType(mini::CameraCalculate::Free);
		pCamera->setEye(&eye.x);
		pCamera->setLookAt(&lookat.x);
		pCamera->createTo3DProjectMatrix();
		pCamera->applyToRenderer(RenderType1);
		pCamera->enable();
		buildCamera(pCamera);
		pCamera->enable();
		SafeRelease(pCamera);
	}

	bool CRenderViewport::apply()
	{
		if (nullptr != mpCamera && isActive())
		{
			auto pRendererPlugin = CRendererEngine::getSinglePtr();
			auto pRenderer = pRendererPlugin->findRenderer(RendererType::FrontRenderer);
			auto drawToTexture = [&]()
			{
				if (beginDraw(pRenderer->getDeviceContext()))
				{
					pRenderer->_render();
					endDraw(pRenderer->getDeviceContext());
				}
				auto pDeferredContext = pRenderer->getDeviceContext();
				ID3D11CommandList* pComandList = nullptr;
				pDeferredContext->FinishCommandList(FALSE, &pComandList);
				GEngine->getDevice()->executeCommandList(pComandList);
				GEngine->getDevice()->useContext(pDeferredContext);
			};
			if (nullptr != pRenderer)
			{
				drawToTexture();
			}
			return true;
		}
		return false;
	}


	bool CRenderViewport::beginDraw(DeviceContext* pConetext)
	{
		if (mRenderNodes.size() > 0)
		{
			auto pRendererPlugin = CRendererEngine::getSinglePtr();
			auto pRenderer = pRendererPlugin->findRenderer(RendererType::FrontRenderer);
			for (auto& item : mPreviewRenderNodes)
			{
				auto pRenderNode = item.first;
				pRenderNode->pushPlaceInfo();
				pRenderNode->getActor()->setRotation((item.second).rotation);
				pRenderNode->getActor()->setScale((item.second).scale);
				pRenderNode->getActor()->setPosition((item.second).point);
				pRenderNode->getActor()->updateMeshUnitMatrix(0);
			}
			mpRenderToTexture->beginRender(CPoint4F(0.0, 0.0, 0.0, 1.0), pConetext);
			mpRendererCamera = pRendererPlugin->getCamera();
			pRendererPlugin->setCamera(mpCamera);
			pRendererPlugin->applyRenderNodes(&mRenderNodes);
			return true;
		}
		return false;
	}

	void CRenderViewport::endDraw(DeviceContext* pConetext)
	{
		auto pRendererPlugin = CRendererEngine::getSinglePtr();
		mpRenderToTexture->endRender(pConetext);
		pRendererPlugin->setCamera(mpRendererCamera);
		pRendererPlugin->applyPreRenderNodes();
		for (auto& item : mPreviewRenderNodes)
		{
			(item.first)->popPlaceInfo();
		}
	}

	void CRenderViewport::removeAllRenderNodes()
	{
		mRenderNodes.clear();
		mPreviewRenderNodes.clear();
	}

	void CRenderViewport::removeRenderNode(CRenderNode* pRenderNode)
	{
		if (nullptr != pRenderNode)
		{
			auto item = mRenderNodes.find(pRenderNode);
			if (mRenderNodes.end() != item)
			{
				mRenderNodes.erase(item);
			}
			auto previewItem = mPreviewRenderNodes.find(pRenderNode);
			if (mPreviewRenderNodes.end() != previewItem)
			{
				mPreviewRenderNodes.erase(previewItem);
			}
		}
	}

	ActorPlaceInfo* CRenderViewport::addRenderNode(CRenderNode* pRenderNode)
	{
		if (nullptr != pRenderNode)
		{
			mini::ActorPlaceInfo info;
			info.scale = pRenderNode->getActor()->getScale();
			info.point = pRenderNode->getActor()->getPosition();
			info.rotation = pRenderNode->getActor()->getRotation();
			auto item = mRenderNodes.find(pRenderNode);
			if (mRenderNodes.end() == item)
			{
				mRenderNodes.insert(pRenderNode);
				mPreviewRenderNodes[pRenderNode] = info;
				return &mPreviewRenderNodes[pRenderNode];
			}
		}
		return nullptr;
	}

	void CRenderViewport::addAllScenRenderNode()
	{
		auto& renderNodes = GEngine->getCurrentScene()->getRenderNodes();
		ActorPlaceInfo info;
		for (auto item : renderNodes)
		{
			info.scale = item->getActor()->getScale();
			info.point = item->getActor()->getPosition();
			info.rotation = item->getActor()->getRotation();
			mRenderNodes.insert(item);
			mPreviewRenderNodes[item] = info;
		}
	}

	CTexture* CRenderViewport::getRenderTexture()
	{
		return  (nullptr != mpRenderToTexture) ? mpRenderToTexture->getRenderTexture() : nullptr;
	}

	int CRenderViewport::getRendererFlag()
	{
		return mRendererFalg;
	}

	void CRenderViewport::setRendererFlag(int flag)
	{
		mRendererFalg = flag;
	}

	CCamera* CRenderViewport::getCamera()
	{
		return mpCamera;
	}

	void CRenderViewport::setCamera(CCamera* pCamear)
	{
		mpCamera = pCamear;
	}

	void CRenderViewport::setMaterial(CMaterialUnit* pMaterialUnit)
	{
		if (mpMaterialUnit != pMaterialUnit)
		{
			SafeRelease(mpMaterialUnit);
			mpMaterialUnit = pMaterialUnit;
			mpMaterialUnit->AddRef();
		}
	}

	void CRenderViewport::setMaterial(const std::string& szFile)
	{
		SafeRelease(mpMaterialUnit);
		if (nullptr == mpMaterialUnit)
		{
			mpMaterialUnit = new CMaterialUnit;
		}
		mpMaterialUnit->setUnitFile(szFile);
		mpMaterialUnit->onInitialise();
	}

	const CMaterialUnit* CRenderViewport::getMaterialUnit() const
	{
		return mpMaterialUnit;
	}

}

